#include "brain.hpp"
#include "neuron.hpp"

using namespace std;

Brain::Brain (uint nInputs, uint nNeuronsPerLayer, uint nLayers, uint nOutputs) {
    m_nInputs = nInputs;
    m_nNeurons = nNeuronsPerLayer * nLayers;
    m_nOutputs = nOutputs;
    m_nNeuronsPerLayer = nNeuronsPerLayer;
    m_inputNeurons = new Neuron[m_nInputs];
    m_bodyNeurons = new Neuron[m_nNeurons];
    m_outputNeurons = new Neuron[m_nOutputs];  //dd

    //cout<<nInputs<<" "<<nNeuronsPerLayer<<" "<<nLayers<<" "<<nOutputs<<endl;

    //1. Init the number of required connections for each Neuron
    //2. Set the connections for body (minus first layer) and output Neurons (the last 2 for loops)
    for(uint i=0; i<m_nInputs; i++){
        m_inputNeurons[i].init(0,0);
    }
    for(uint i=0; i<m_nNeuronsPerLayer; i++){
        m_bodyNeurons[i].init(m_nInputs,0);

        for(uint j=0; j<m_nInputs; j++){
            m_bodyNeurons[i].getConnections()[j].pNeuron = & m_inputNeurons[j];
            m_bodyNeurons[i].getConnections()[j].weight = rand()%1000/500.0 - 1.0;
        }
    }
    for(uint i=m_nNeuronsPerLayer; i<m_nNeurons; i+=m_nNeuronsPerLayer){
        for(uint p=0; p<m_nNeuronsPerLayer; p++){
            m_bodyNeurons[i+p].init(m_nNeuronsPerLayer, 0);
            for(uint j=0; j<m_nNeuronsPerLayer; j++){
                m_bodyNeurons[i+p].getConnections()[j].pNeuron = &m_bodyNeurons[i+p - m_nNeuronsPerLayer];
                m_bodyNeurons[i+p].getConnections()[j].weight = rand()%1000/500.0 - 1.0;
            }
        }
    }
    for(uint i=0; i<m_nOutputs; i++){
        m_outputNeurons[i].init(m_nNeuronsPerLayer, 0);
        for(uint j=0; j<m_nNeuronsPerLayer; j++){
            m_outputNeurons[i].getConnections()[j].pNeuron = &m_bodyNeurons[m_nNeurons - m_nNeuronsPerLayer + j];
            m_outputNeurons[i].getConnections()[j].weight = rand()%1000/500.0 - 1.0;
        }
    }
}

Brain::~Brain () {
    delete[] m_inputNeurons;
    delete[] m_bodyNeurons;
    delete[] m_outputNeurons;
}

void Brain::compute(float * inputs, float * outputs){
    for(uint i=0; i<m_nInputs; i++){
        m_inputNeurons[i].setState(inputs[i]);
    }
    for(uint i=0; i<m_nNeurons; i++){
        m_bodyNeurons[i].compute();
    }
    for(uint i=0; i<m_nOutputs; i++){
        m_outputNeurons[i].compute();
        outputs[i] = m_outputNeurons[i].getState();
    }
}

int Brain::copy (Brain * source) {
    return 1;
}

void Brain::mutate (int rate) {
    for (uint i = 0; i < m_nNeurons; i++) {
        m_bodyNeurons[i].mutate(rate);
    }
}

void Brain::crossover (Brain * parent1, Brain * parent2) {
    /* TODO: implement */

}

void Brain::saveBrain(const char * filename){
    ofstream brainFile;
    brainFile.open(filename, ios::out | ios::trunc );

    if(brainFile.is_open()){
        //Number of Connections on a neuron in first layer
        brainFile << m_bodyNeurons[0].getNumOfConn() << "\n";

        //Number of Connections on a neuron after first layer
        brainFile << m_bodyNeurons[m_nNeuronsPerLayer].getNumOfConn() << "\n";

        //First Layer
        for(uint i=0; i<m_nNeuronsPerLayer; i++){
            for(uint j=0; j<m_nInputs; j++){
                brainFile << m_bodyNeurons[i].getConnections()[j].weight << " ";
            }
        }
        brainFile << "\nDoneFirstLayer\n";

        //Body Neurons
        for(uint i=m_nNeuronsPerLayer; i<m_nNeurons; i++){
            for(uint j=0; j<m_nNeuronsPerLayer; j++){
                brainFile << m_bodyNeurons[i].getConnections()[j].weight << " ";
            }
            brainFile<<"\n";
        }

        //Output Neurons
        for(uint i=0; i<m_nOutputs; i++){
            for(uint j=0; j<m_nNeuronsPerLayer; j++){
                brainFile << m_outputNeurons[i].getConnections()[j].weight << " ";
            }
        }
        brainFile.close();
    }
}

void Brain::loadBrain(const char * filename){
    string line;
    stringstream sstr;
    ifstream brainFile;
    brainFile.open(filename, ios::in );
    if(brainFile.is_open()){
        while(brainFile.good()){
            getline(brainFile, line);

        }
        brainFile.close();
    }else{
        cout << "Unable to open file" << endl;
    }

}
